/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.security.token.strategy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.tianyun.cloud.security.configuration.SecurityProperties;
import org.tianyun.cloud.security.token.common.AccessToken;
import org.tianyun.cloud.security.token.common.Authentication;
import org.tianyun.cloud.security.token.enhance.TokenSecretEnhance;
import org.tianyun.cloud.security.token.store.TokenStore;
import org.tianyun.cloud.utils.DateUtils;

import java.security.Principal;
import java.util.Date;

/**
 * token策略，用于创建、解析token
 *
 * @auther ebert_chan
 */
public abstract class AbstractTokenStrategy implements TokenStrategy {

    protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    private SecurityProperties securityProperties;

    private TokenStore tokenStore;

    private TokenSecretEnhance tokenSecretEnhance;

    /**
     * @param securityProperties
     * @param tokenStore
     */
    public AbstractTokenStrategy(SecurityProperties securityProperties, TokenStore tokenStore) {
        this.securityProperties = securityProperties;
        this.tokenStore = tokenStore;
    }

    /**
     * 生成token
     *
     * @param authentication
     * @return String token string value
     * @auther ebert_chan
     */
    public abstract String generateToken(Authentication authentication);

    /**
     * 提取token
     *
     * @param value
     * @return Principal
     * @auther ebert_chan
     */
    public abstract Principal extractToken(String value);

    /*
     * @see org.tianyun.cloud.security.token.service.TokenServices#createAccessToken(org.tianyun.cloud.security.token.common.Authentication)
     */
    @Override
    public AccessToken createAccessToken(Authentication authentication) {
        String value = generateToken(authentication);
        AccessToken accessToken = new AccessToken(value, authentication.getRawValue());
        if (securityProperties.getTokenStrategy().getAccessTokenValiditySeconds() != null && securityProperties.getTokenStrategy().getAccessTokenValiditySeconds() > 0) {
            accessToken.setExpiresIn(securityProperties.getTokenStrategy().getAccessTokenValiditySeconds());
            accessToken.setExpiration(DateUtils.addSeconds(new Date(), securityProperties.getTokenStrategy().getAccessTokenValiditySeconds()));
        }

        tokenStore.storeAccessToken(authentication, accessToken);

        return accessToken;
    }

    /*
     * @see org.tianyun.cloud.security.token.strategy.TokenStrategy#restoreRawValue(org.tianyun.cloud.security.token.common.Authentication)
     */
    @Override
    public void restoreRawValue(Authentication authentication) {
        AccessToken accessToken = this.getAccessToken(authentication);
        if (accessToken == null) {
            this.createAccessToken(authentication);
            return;
        }
        accessToken.setRawValue(authentication.getRawValue());
        tokenStore.storeAccessToken(authentication, accessToken);
    }

    /*
     * @see org.tianyun.cloud.security.token.service.TokenServices#getAccessToken(java.security.Principal)
     */
    @Override
    public AccessToken getAccessToken(Principal principal) {
        AccessToken accessToken = tokenStore.getAccessToken(principal);
        return accessToken;
    }

    /*
     * @see org.tianyun.cloud.security.token.service.TokenServices#removeAccessToken(java.security.Principal)
     */
    @Override
    public void removeAccessToken(Principal principal) {
        tokenStore.removeAccessToken(principal);
    }

    /*
     * @see org.tianyun.cloud.security.token.service.TokenServices#readAccessToken(java.lang.String)
     */
    @Override
    public AccessToken readAccessToken(String value) {
        Principal principal = this.extractToken(value);
        if (principal == null) {
            LOGGER.info("invalid token: {}", value);
            return null;
        }

        AccessToken accessToken = tokenStore.getAccessToken(principal);
        return accessToken;

    }

    /**
     * 获取增加的key
     *
     * @param principal
     * @param secret    原始key
     * @return
     * @auther ebert_chan
     */
    public String getEnhanceKey(Principal principal, String secret) {
        if (getTokenSecretEnhance() == null || !StringUtils.hasLength(principal.getName())) {
            return secret;
        }

        String randomKey = getTokenSecretEnhance().getRandomKey(principal);

        if (randomKey == null) {
            return secret;
        }

        return secret + randomKey;
    }

    public SecurityProperties getSecurityProperties() {
        return securityProperties;
    }

    public TokenStore getTokenStore() {
        return tokenStore;
    }

    public TokenSecretEnhance getTokenSecretEnhance() {
        return tokenSecretEnhance;
    }

    public void setTokenSecretEnhance(TokenSecretEnhance tokenSecretEnhance) {
        this.tokenSecretEnhance = tokenSecretEnhance;
    }

}
